//
// Created by Immortal on 4/23/2021.
//

#include "Server.h"


Server::Server(uint16_t uPort)
{
    m_lfd = 0;
    m_epfd = 0;
    m_pmapData = new std::map<int, Server::PDATA>;
    m_pqueTask = new std::queue<Server::PDATA>;
    //请求头限制
    m_nReqHeadLimit = 4096;
    //请求内容限制
    m_nReqContentLimit = 8192;

    //忽略SIGPIPE 防止send出错
    signal(SIGPIPE, SIG_IGN);

    //初始化socket
    InitSocket(uPort);
}

Server::~Server()
{
    //关闭
    close(m_lfd);
    close(m_epfd);

    //释放
    if(m_pmapData != NULL)
    {
        delete m_pmapData;
        m_pmapData = NULL;
    }

    if(m_pqueTask != NULL)
    {
        delete m_pqueTask;
        m_pqueTask = NULL;
    }
}


//初始化socket
bool Server::InitSocket(uint16_t uPort)
{
    //创建socket
    m_lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(m_lfd == -1)
    {
        perror("socket error:");
        return false;
    }

    //填充结构
    m_SvrAddr.sin_addr.s_addr = INADDR_ANY;
    m_SvrAddr.sin_port = htons(uPort);
    m_SvrAddr.sin_family = AF_INET;

    //地址复用
    unsigned value = 1;
    setsockopt(m_lfd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));

    //绑定
    if(bind(m_lfd, (struct sockaddr*)&m_SvrAddr, sizeof(m_SvrAddr)) == -1)
    {
        perror("bind error:");
        return false;
    }

    //创建epoll
    m_epfd = epoll_create(30);
    if(m_epfd == -1)
    {
        perror("epoll_create error:");
        return false;
    }

    //监听
    if(listen(m_lfd, 30) == -1)
    {
        perror("listen error:");
        return false;
    }

    //添加到epoll里并返回
    return CtlFd(m_epfd, EPOLL_CTL_ADD, m_lfd, EPOLLIN);
}

//设置非阻塞
bool Server::SetNonBlock(int cfd)
{
    //获取
    int nFlags = fcntl(cfd, F_GETFL, 0);
    if(nFlags < 0)
    {
        return false;
    }

    //加入
    nFlags |= O_NONBLOCK;
    //设置
    if(fcntl(cfd, F_SETFL, nFlags) < 0)
    {
        return false;
    }

    return true;
}

//设置处理函数
void Server::SetHandlingFunc(PHANDLINGFUNC pHandlingFunc, void* pArg)
{
    m_pHandlingFunc = pHandlingFunc;
    m_pArg = pArg;
}

//执行
bool Server::Exec()
{
    //获取核心数 IO密集
    int nCoreCnt = get_nprocs_conf() * 2;
    for(int i = 0; i < nCoreCnt; i++)
    {
        //创建工作者线程
        std::thread t(&Server::WorkerProc, this);
        t.detach();
    }


    epoll_event evs[1024] = { 0 };
    while(true)
    {
        //等待
        int n = epoll_wait(m_epfd, evs, 1024, -1);
        if(n == -1)
        {
            if(errno == EINTR)
            {
                continue;;
            }
            break;
        }

        //遍历
        for(int i = 0; i < n; i++)
        {
            int cfd = evs[i].data.fd;

            //判断是否为监听描述符
            if(cfd == m_lfd)
            {
                //接收
                AcceptCli();
            }
            else
            {
                //处理
                HandlingCli(cfd);
            }
        }
    }


}

//设置请求头最大限制
void Server::SetReqHeadLimit(int nSize)
{
    m_nReqHeadLimit = nSize;
}

//设置请求内容最大限制
void Server::SetReqContentLimit(int nSize)
{
    m_nReqContentLimit = nSize;
}

//接受客户端
bool Server::AcceptCli()
{
    sockaddr_in CliAddr = { 0 };
    socklen_t CliAddrLen = sizeof(sockaddr_in);

    //接受
    int cfd = accept(m_lfd, (sockaddr*)&CliAddr, &CliAddrLen);
    if(cfd == -1)
    {
        //出错
        perror("accept error:");
        return false;
    }

    //设置非阻塞
    if(SetNonBlock(cfd) == false)
    {
        return false;
    }

    //添加到epoll里
    if(CtlFd(m_epfd, EPOLL_CTL_ADD, cfd, EPOLLIN) == false)
    {
        return false;
    }

    //实例化一个数据
    auto pData = new Server::DATA;

    //填充数据结构体
    pData->cfd = cfd;
    memcpy(&pData->CliAddr, &CliAddr, sizeof(sockaddr_in));
    pData->nSentHead = 0;
    pData->nSentContent = 0;
    pData->ds = DS_READ_HEAD;

    //添加进数据map里
    (*m_pmapData)[cfd] = pData;

    std::cout << pData->cfd << " connected" << std::endl;

    return true;
}


//处理客户端
bool Server::HandlingCli(int cfd)
{
    //从map数据中查找
    auto it = m_pmapData->find(cfd);
    if(it == m_pmapData->end())
    {
        //不存在则关闭
        close(cfd);
        return true;
    }

    //从epoll中删除
    if(CtlFd(m_epfd, EPOLL_CTL_DEL, cfd, 0) == false)
    {
        //删除失败
        return false;
    }

    PDATA pData = it->second;

    //判断是否出错和完成
    if(pData->ds == DS_ERROR)
    {
        DataError(this, pData);
        return true;
    }
    else if(pData->ds == DS_FINISH)
    {
        ResFinish(this, pData);
        return true;
    }

    //添加到任务队列
    m_pqueTask->push(it->second);

    //通知
    m_cond.notify_one();

    return true;
}


//修改fd
bool Server::CtlFd(int epfd, int op, int fd, uint32_t events)
{
    epoll_event ev = { 0 };
    ev.data.fd = fd;
    ev.events = events;

    return epoll_ctl(epfd, op, fd, &ev) == -1 ? false : true;
}


//工作者线程
void Server::WorkerProc(Server* pServer)
{

    while(1)
    {
        //获取锁
        std::unique_lock<std::mutex> lock(pServer->m_mutex);

        //判断任务队列是否为空
        if(pServer->m_pqueTask->empty())
        {
            //等待
            pServer->m_cond.wait(lock);
        }

        //取出任务
        PDATA pData = pServer->m_pqueTask->front();

        //从队列中删除取出的任务
        pServer->m_pqueTask->pop();

        //释放锁
        lock.release()->unlock();
		
		if(pData == NULL)
		{
			continue;
		}

        //处理
        Server::_DATA_STATUS dsPrev;

        do
        {
            dsPrev = pData->ds;

            switch (pData->ds) {
                //读取 请求头
                case DS_READ_HEAD:
                    Server::ReadHead(pServer, pData);
                    break;
                //读取 内容
                case DS_READ_CONTENT:
                    Server::ReadContent(pServer, pData);
                    break;
                //处理
                case DS_HANDLING:
                    pServer->m_pHandlingFunc(&pData->http, pServer->m_pArg);
                    pData->ds = DS_WRITE_HEAD;
                    break;
                //写入 请求头
                case DS_WRITE_HEAD:
                    Server::SendHead(pServer, pData);
                    break;
                //写入 内容
                case DS_WRITE_CONTENT:
                    Server::SendContent(pServer, pData);
                    break;
                //写入 文件
                case DS_WRITE_FILE:
                    Server::SendFile(pServer, pData);
                    break;
            }

        }while(dsPrev < pData->ds && pData->ds != DS_ERROR && pData->ds != DS_FINISH);

        //判断上一次数据类型 从而监听不同事件
        if(dsPrev == DS_READ_HEAD || dsPrev == DS_READ_CONTENT)
        {
            //出错就绑定写，如果改成读会无法触发
            if(pData->ds == DS_ERROR)
            {
                //写
                Server::CtlFd(pServer->m_epfd, EPOLL_CTL_ADD, pData->cfd, EPOLLOUT);
            }
            else
            {
                //读
                Server::CtlFd(pServer->m_epfd, EPOLL_CTL_ADD, pData->cfd, EPOLLIN);
            }
        }
        else
        {
            //写
            Server::CtlFd(pServer->m_epfd, EPOLL_CTL_ADD, pData->cfd, EPOLLOUT);
        }

    }

}

//数据错误
void Server::DataError(Server* pServer, Server::PDATA pData)
{
    std::cout << pData->cfd << " closed" << std::endl;

    //从map中删除
    pServer->m_pmapData->erase(pData->cfd);
    //断开
    close(pData->cfd);
    //释放数据
    delete pData;
}


//读取数据
int Server::ReadData(int epfd, int cfd, char* buf, int n, bool& bIsErr)
{
    bIsErr = false;

    //读取数据
    int nLen = recv(cfd, buf, n, 0);

    //判断是否出错
    if (nLen == -1)
    {
        //出错
        if (errno == EAGAIN)
        {
            //不用管
            bIsErr = false;
        }
        else
        {
            //断开
            bIsErr = true;
        }
    }
    else if (nLen == 0)
    {
        //断开
        bIsErr = true;
    }

    //读取成功
    return nLen;
}


//读取请求头
void Server::ReadHead(Server* pServer, Server::PDATA pData)
{
    while(1)
    {
        char buf[1024] = { 0 };
        bool bIsErr = false;
        //读取数据
        int n = Server::ReadData(pServer->m_epfd, pData->cfd, buf, sizeof(buf), bIsErr);
        if(n <= 0)
        {
            //判断错误
            if(bIsErr == true)
            {
                //有错，设置数据状态
                pData->ds = DS_ERROR;
            }

            break;
        }

        //获取结尾索引
        int nIndex = OpStr::FindStr(buf, n, "\r\n\r\n", 4);
        if(nIndex == -1)
        {
            //没有结尾 直接追加到请求头
            pData->strReqHead.append(buf, n);

            //判断请求头是否超出限制
            if(pData->strReqHead.size() > pServer->m_nReqHeadLimit)
            {
                //解析错误
                pData->ds = DS_ERROR;
                break;
            }
        }
        else
        {
            //有结尾
            //结尾前的数据 追加到请求头
            pData->strReqHead.append(buf, nIndex + 4);

            //判断请求头是否超出限制
            if(pData->strReqHead.size() > pServer->m_nReqHeadLimit)
            {
                //解析错误
                pData->ds = DS_ERROR;
                break;
            }

            //解析http请求头
            //判断是否解析出错
            if(pData->http.Read(pData->strReqHead.c_str()) == false)
            {
                //解析错误
                pData->ds = DS_ERROR;
                break;
            }

            //结尾后的数据 追加到请求内容
            pData->strReqContent.append(buf, nIndex + 4, n - (nIndex + 4));
            //设置数据状态
            pData->ds = DS_READ_CONTENT;

            //判断请求内容大小
            if(pData->http.GetReqContentSize() == 0)
            {
                //请求内容大小没有 - 直接处理
                pData->ds = DS_HANDLING;
            }
            //判断请求头是否超出限制
            else if(pData->strReqContent.size() > pServer->m_nReqContentLimit)
            {
                //解析错误
                pData->ds = DS_ERROR;
                printf("error1");
                break;
            }
            //请求内容是否已接收完毕
            else if(pData->strReqContent.size() == pData->http.GetReqContentSize())
            {
                //已接收完毕 - 直接处理
                pData->ds = DS_HANDLING;
                //设置请求内容到http
                pData->http.SetReqContentAddr(pData->strReqContent);
            }
            //如果是数据超过了，就出错
            else if(pData->strReqContent.size() > pData->http.GetReqContentSize())
            {
                //解析错误
                pData->ds = DS_ERROR;
                break;
            }

            break;
        }

    }
}

//读取内容
void Server::ReadContent(Server* pServer, Server::PDATA pData)
{
    while(1)
    {
        char buf[1024] = { 0 };
        bool bIsErr = false;

        //读取数据
        int n = Server::ReadData(pServer->m_epfd, pData->cfd, buf, sizeof(buf), bIsErr);
        if(n <= 0)
        {
            //判断错误
            if(bIsErr == true)
            {
                //有错，设置数据状态
                pData->ds = DS_ERROR;
            }
            break;
        }

        //计算新的大小
        int nNewSize = pData->strReqContent.size() + n;

        //判断 新请求内容大小 是否超出总大小 或者 是否超出内容限制
        if(nNewSize > pData->http.GetReqContentSize() || nNewSize > pServer->m_nReqContentLimit)
        {
            //出错 -- 这里等待后期优化，可以不用出错 - 实现keep-alive
            pData->ds = DS_ERROR;
            printf("errror2\n");
            break;
        }

        //追加数据到请求内容容器
        pData->strReqContent.append(buf, n);

        //判断是否读取完毕
        if(pData->strReqContent.size() == pData->http.GetReqContentSize())
        {
            //读取完毕，设置状态为处理
            pData->ds = DS_HANDLING;
            //设置请求内容到http
            pData->http.SetReqContentAddr(pData->strReqContent);
            break;
        }

    }
}


//发送数据
int Server::SendData(int epfd, int cfd, const char* buf, int n, bool& bIsErr)
{
    bIsErr = false;

    //发送
    int nLen = send(cfd, buf, n, 0);

    //判断是否出错
    if (nLen == -1)
    {
        //出错
        if (errno == EAGAIN)
        {
            //不用管
            bIsErr = false;
        }
        else
        {
            //断开
            bIsErr = true;
        }
    }
    else if (nLen == 0)
    {
        //断开
        bIsErr = true;
    }

    return nLen;
}

//发送数据
bool Server::SendData(int epfd, int cfd, std::string& strResData, int* pSentData, bool& bIsErr)
{
    bIsErr = false;

    int n = 0;
    do
    {
        //发送
        n = send(cfd, strResData.c_str() + *pSentData, strResData.size() - *pSentData, 0);

        //判断是否出错
        if (n == -1)
        {
            //出错
            if (errno == EAGAIN)
            {
                //不用管
                bIsErr = false;
                break;
            }
            else
            {
                //断开
                bIsErr = true;
                break;
            }
        }
        else if (n == 0)
        {
            //断开
            bIsErr = true;
            break;
        }

        //修改已发送大小
        *pSentData += n;

        //判断是否发送完
        if(*pSentData == strResData.size())
        {
            bIsErr = false;
            return true;
        }

    }while(n);

    return false;
}

//发送请求头
void Server::SendHead(Server* pServer, Server::PDATA pData)
{
    //判断响应头是否为空
    if(pData->strResHead.empty())
    {
        //获取请求头
        pData->http.GetResHead(pData->strResHead, pData->http.GetResFileSize());
    }

    //发送
    bool bIsErr = false;
    if(Server::SendData(pServer->m_epfd, pData->cfd, pData->strResHead, &(pData->nSentHead), bIsErr) == false)
    {
        //判断错误
        if(bIsErr == true)
        {
            //有错，设置数据状态
            pData->ds = DS_ERROR;
        }
        return;
    }

    //已发送完
    //判断响应类型
    if(pData->http.GetResType())
    {
        //数据
        pData->ds = DS_WRITE_CONTENT;
    }
    else
    {
        //文件
        pData->ds = DS_WRITE_FILE;
        //判断文件是否存在
        if(pData->http.GetResFileSize() == -1)
        {
            //文件不存在 直接完成
            pData->ds = DS_FINISH;
        }
    }

}

//发送内容
void Server::SendContent(Server* pServer, Server::PDATA pData)
{
    //判断响应头是否为空
    if(pData->strResContent.empty())
    {
        //获取响应内容
        pData->strResContent = pData->http.GetResContent();
    }

    //发送
    bool bIsErr = false;
    if(Server::SendData(pServer->m_epfd, pData->cfd, pData->strResContent, &(pData->nSentContent), bIsErr) == false)
    {
        //判断错误
        if(bIsErr == true)
        {
            //有错，设置数据状态
            pData->ds = DS_ERROR;
        }
        return;
    }

    //已发送完
    pData->ds = DS_FINISH;
}

//发送文件
void Server::SendFile(Server* pServer, Server::PDATA pData)
{
    //打开文件
    FILE* wj = fopen(pData->http.GetResContent(), "rb");
    if(wj == NULL)
    {
        pData->ds = DS_ERROR;
        //断开连接
        close(pData->cfd);
        return;
    }

    //获取响应文件大小
    int nResFileSize = pData->http.GetResFileSize();

    //循环发送
    while(pData->nSentContent < pData->http.GetResFileSize())
    {
        //定位
        fseek(wj, pData->nSentContent, SEEK_SET);

        //读取
        char buf[1024] = { 0 };
        int n = fread(buf, 1, sizeof(buf), wj);

        //发送
        bool bIsErr = false;
        n = Server::SendData(pServer->m_epfd, pData->cfd, buf, n, bIsErr);
        //发送数据
        if(n <= 0)
        {
            //判断错误
            if(bIsErr == true)
            {
                //有错，设置数据状态
                pData->ds = DS_ERROR;
            }
            break;
        }

        pData->nSentContent += n;

        //判断是否发送完毕
        if(pData->nSentContent == nResFileSize)
        {
            //完成
            pData->ds = DS_FINISH;
            break;
        }
    }

    fclose(wj);
}


//响应完成
void Server::ResFinish(Server* pServer, Server::PDATA pData)
{
    if(pData->http.GetKeepAlive() == false)
    {
        //通过数据错误处理函数来断开连接
        DataError(pServer, pData);
    }
    else
    {
        //保持连接，清空数据
        pData->ds = Server::DS_READ_HEAD;
        pData->strReqHead.clear();
        pData->strReqContent.clear();
        pData->strResHead.clear();
        pData->strResContent.clear();
        pData->nSentHead = 0;
        pData->nSentContent = 0;

        //再次监听
        if(Server::CtlFd(pServer->m_epfd, EPOLL_CTL_ADD, pData->cfd, EPOLLIN) == false)
        {
            //通过数据错误处理函数来断开连接
            DataError(pServer, pData);
        }
    }
}
